-module(erlydb_test).
-export(
   [init/0,
    test/0,
    recompile/0]).

-define(L(Obj), io:format("LOG ~w ~p\n", [?LINE, Obj])).
-define(S(Obj), io:format("LOG ~w ~s\n", [?LINE, Obj])).


init() ->
    %% connect to the database
    erlydb:connect(mysql, "localhost", "root", "password", "test"),

    %% generate the abstraction layer modules
    erlydb:code_gen([language, project, developer]).

test() ->
    init(),
    

    %% clean up old records
    erlydb:q("delete from language;"),
    erlydb:q("delete from project;"),
    erlydb:q("delete from developer;"),
    erlydb:q("delete from developer_project"),
    
    %% Create some new records
    Languages = 
	[language:new("Erlang",
		      "A fun and productive functional language with " ++
		      "top-notch concurrency features designed for "++
		      "building scalable, fault-tolerant systems. " ++
		      "Erlang is an excellent choice for building "++
		      "anything from websites like hamsterster.com " ++
		      "to commercial phone switches.",
		      "functional/dynamic/concurrent", 1981),
	 language:new("Java",
		      "An OO language designed to make the lives of " ++
		      "C++ programmers less painful.",
		      "OO/static", 1992),
	 language:new("Ruby",
		      "An OO language designed to make the lives of " ++
		      "Java programmers less painful.",
		      "OO/script/dynamic", 1995)],

    %% Save the records in the database and collect the updated
    %% tuples.
    [Erlang, Java, Ruby] =
	lists:map(
	  fun(Language) ->
		  %% executes an INSERT statement
		  {ok, Lang} = language:save(Language),
		  Lang
	  end, Languages),

    %% demonstrate getters
    "Erlang" = language:name(Erlang),
    "functional/dynamic/concurrent" = language:paradigm(Erlang),
    1981 = language:creation_year(Erlang),

    %% demonstrate setter
    J1 = language:creation_year(Java, 1993),

    %% executes an UPDATE statement
    {ok, J2} = language:save(J1),

    1993 = language:creation_year(J2),


    %% Lets run some queries
    {ok, E1} = language:find_id(language:id(Erlang)),
    true = E1 == Erlang,
    
    {ok, [E2]} = language:find_where("name = 'Erlang'"),
    true = E2 == Erlang,

    {ok, [E3]} = language:find(
		   "WHERE paradigm = 'functional/dynamic/concurrent' LIMIT 1"),
    true = E3 == Erlang,
    {ok, [E4, J4, R4]} = language:find_all(),
    true =
	E4 == Erlang andalso 
	J4 == J1 andalso 
	R4 == Ruby,
    
    %% Let's make some projects
    
    Yaws = project:new(
	     "Yaws", "A beautiful web server written in Erlang", Erlang),
    Ejabberd = project:new(
		 "ejabberd", "The best Jabber server, hands down", Ruby),
    OpenPoker = 
	project:new("OpenPoker", "A scalable, fault-tolerant poker server",
		  Erlang),


    %% We call language:id just to demonstrate that constructors accept
    %% both related tuples or, alternatively, their id's. This example
    %% would behave identically if we used the Java variable directly.
    Tomact = 
	project:new("Tomcat", "A Java Server with XML config files",
		    language:id(Java)),

    JBoss =
	project:new("JBoss", "A Java Application Server with more XML files",
			Java),
    Spring =
	project:new("Spring Framework",
		    "A Java IoC Framework oozing XML love", Java),

    
    Mongrel =
	project:new("Mongerl", "A web server with a funny name", Ruby),
    Rails =
	project:new("Ruby on Rails",
		    "A nice integrated web framework for building " ++
		    "CRUD websites.", Ruby),
    Ferret = project:new("Ferret",
			 "A Ruby port of Apache Lucene. It would be nice " ++
			 "if someone ported it to Erlang", Ruby),
    Gruff = project:new("Gruff",
			"A Ruby library for easy graph generation. " ++
			"An Erlang implementation would be nice as well",
			Ruby),

    Projects =  [Yaws, Ejabberd, OpenPoker, Tomact, JBoss, Spring,
		 Mongrel, Rails, Ferret, Gruff],
    
    %% Insert our projects into the database
    [Yaws1, Ejabberd1, OpenPoker1 | _Rest] =
	lists:map(
	  fun(Project) ->
		  {ok, P1} = project:save(Project),
		  P1
	  end, Projects),
    
    %% lets get the language associated with Yaws
    {ok, Erlang2} = project:language(Yaws1),
    true = (Erlang2 == Erlang),
    
    %% now let's correct a grave error
    {ok, Ejabberd2} = project:save(project:language(Ejabberd1, Erlang)),
    true = (language:id(Erlang) == project:language_id(Ejabberd2)),


    %% let's get all the projects for a language
    {ok, [Yaws3, Ejabberd3, OpenPoker3]} = language:projects(Erlang),

    true =
	Yaws3 == Yaws1
	andalso Ejabberd3 == Ejabberd2
	andalso OpenPoker3 == OpenPoker1,

    %% fancier project queries
    {ok, [Yaws4]} = language:projects_where(
		      Erlang, "name='Yaws'"),
    true = Yaws4 == Yaws1,

    {ok, [Yaws5]} = language:projects_with(
		      Erlang, "limit 1"),
    true = Yaws4 == Yaws5,

    {ok, [Ejabberd4]} = language:projects(
			  Erlang, "name like '%e%'", "limit 1"),
    true = Ejabberd4 == Ejabberd3,


    %% Let's show off some many-to-many features

    %% First, add some more projects
    [OTP, Mnesia] =
	lists:map(
	  fun(Proj) ->
		  {ok, P1} = project:save(Proj), 
		  P1
	  end,
	  [project:new("OTP", "The Open Telephony Platform", Erlang),
	   project:new("Mnesia", "A distributed database " ++
		       "engine written in Erlang", Erlang)]),

    %% Next, add some developers
    [Joe, Ulf, Klacke] =
	lists:map(
	  fun(Developer) ->
		  {ok, D1} =
		      developer:save(Developer),
		  D1
	  end,
	  [developer:new("Joe Armstrong", "Sweden"),
	   developer:new("Ulf Wiger", "Sweden"),
	   developer:new("Claes (Klacke) Wikstrom", "Sweden")]),

    %% Add some developers to our projects
    ok = project:add_developer(OTP, Joe),
    ok = project:add_developer(OTP, Klacke),
    ok = project:add_developer(OTP, Ulf),
    
    %% Add some projects to our developers
    ok = developer:add_project(Klacke, Yaws1),
    ok = developer:add_project(Klacke, Mnesia),
    ok = developer:add_project(Ulf, Mnesia),

    
    %% basic SELECT query
    {ok, [Joe, Ulf, Klacke]} =
	project:developers(OTP),

    %% fancier SELECT queries
    {ok, [Ulf, Klacke]} =
	project:developers(Mnesia),
    {ok, [Ulf]} = project:developers_with(Mnesia,
					  "limit 1"),
    {ok, [Klacke]} =
	project:developers_where(Mnesia,
				 "name like 'Claes%'"),
    
    %% SELECT query, from the other direction
    {ok, [Yaws1, OTP, Mnesia]} =
	developer:projects(Klacke),

    %% Klacke, nothing personal here :)
    ok = developer:remove_project(Klacke, Yaws1),
    {ok, [OTP, Mnesia]} = developer:projects(Klacke),
    ok = developer:remove_project(Klacke, OTP),
    {ok, [Mnesia]} = developer:projects(Klacke),
    ok = developer:remove_project(Klacke, Mnesia),
    {ok, []} = developer:projects(Klacke),

    %% I forgot to show you can delete records, too :)
    ok = language:delete(Java),
	 
    ok.

recompile() ->    
    lists:foreach(fun(A) -> compile:file(A) end, ["../src/erlydb.erl", "../src/erlydb_base.erl","../src/erlydb_mysql.erl", "../test/erlydb_test.erl"]).

%lists:foreach(fun(A) -> compile:file(A) end, ["../src/erlydb.erl", "../src/erlydb_base.erl","../src/erlydb_mysql.erl", "../test/erlydb_test.erl"]).
